package com.studio49.exmple.common.util;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;


public class AESUtils {

	/**
	 * 要导出的密钥长度
	 */
	private final static int KEYSIZE = Integer
			.parseInt(getConfigByKey("aesutils.keySize"));
	/**
	 * 迭代计数
	 */
	private final static int ITERATIONCOUNT = Integer
			.parseInt(getConfigByKey("aesutils.iterationCount"));;
	/**
	 * 偏移 CryptoJS.lib.WordArray.random(128/8)使用此方法生成,需要与后台使用的一致
	 */
	private final static String IV = getConfigByKey("aesutils.iv");
	/**
	 * 加密Key
	 */
	private final static String SALT = getConfigByKey("aesutils.salt");
	/**
	 * 密钥 MD5 加密
	 */
	private final static String PASSPHRASE = getConfigByKey("aesutils.passphrase");

	private static Cipher cipher = null;

	private AESUtils() {
	}

	static {
		try {
			cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw fail(e);
		}
	}

	private static String getConfigByKey(String key) {
		ResourceBundle config = ResourceBundle.getBundle("aes.aesConfig",
				Locale.getDefault(), AESUtils.class.getClassLoader());
		return config.getString(key);
	}

	/**
	 * 加密
	 * 
	 * @param plaintext
	 * @return
	 */
	public static String encrypt(String plaintext) {
		try {
			SecretKey key = generateKey(SALT, PASSPHRASE);
			byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, IV,
					plaintext.getBytes("UTF-8"));
			return base64(encrypted);
		} catch (UnsupportedEncodingException e) {
			throw fail(e);
		}
	}

	/**
	 * 加密
	 * 
	 * @param plaintext
	 * @return 返回密文的16进制字符串
	 */
	public static String encryptHexStr(String plaintext) {
		try {
			SecretKey key = generateKey(SALT, PASSPHRASE);
			byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, IV,
					plaintext.getBytes("UTF-8"));
			return bytesToHexStr(base64(encrypted).getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			throw fail(e);
		}
	}

	/**
	 * 解密
	 * 
	 * @param ciphertext
	 * @return 把16进制的字符串解密
	 */
	public static String decryptHexStr(String ciphertext) {
		try {
			ciphertext = hexStr2Str(ciphertext);
			SecretKey key = generateKey(SALT, PASSPHRASE);
			byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, IV,
					base64(ciphertext));
			return new String(decrypted, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw fail(e);
		}
	}

	/**
	 * 解密
	 * 
	 * @param ciphertext
	 * @return
	 */
	public static String decrypt(String ciphertext) {
		try {
			SecretKey key = generateKey(SALT, PASSPHRASE);
			byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, IV,
					base64(ciphertext));
			return new String(decrypted, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw fail(e);
		}
	}

	private static byte[] doFinal(int encryptMode, SecretKey key, String iv,
			byte[] bytes) {
		try {
			cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
			return cipher.doFinal(bytes);
		} catch (InvalidKeyException | InvalidAlgorithmParameterException
				| IllegalBlockSizeException | BadPaddingException e) {
			throw fail(e);
		}
	}

	private static SecretKey generateKey(String salt, String passphrase) {
		try {
			SecretKeyFactory factory = SecretKeyFactory
					.getInstance("PBKDF2WithHmacSHA1");
			KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt),
					ITERATIONCOUNT, KEYSIZE);
			SecretKey key = new SecretKeySpec(factory.generateSecret(spec)
					.getEncoded(), "AES");
			return key;
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw fail(e);
		}
	}

	private static String base64(byte[] bytes) {
		return Base64.encodeBase64String(bytes);
	}

	private static byte[] base64(String str) {
		return Base64.decodeBase64(str);
	}

	private static byte[] hex(String str) {
		try {
			return Hex.decodeHex(str.toCharArray());
		} catch (DecoderException e) {
			throw new IllegalStateException(e);
		}
	}

	private static IllegalStateException fail(Exception e) {
		return new IllegalStateException(e);
	}

	/**
	 * 把加密后的密文转成16进行成最终密文
	 * 
	 * @param bArray
	 * @return
	 */
	public static final String bytesToHexStr(byte[] bArray) {
		StringBuffer sb = new StringBuffer(bArray.length);
		String sTemp;
		for (int i = 0; i < bArray.length; i++) {
			sTemp = Integer.toHexString(0xFF & bArray[i]);
			if (sTemp.length() < 2)
				sb.append(0);
			sb.append(sTemp.toUpperCase());
		}
		return sb.toString();
	}

	/**
	 * 把 16进行的字符串转为10进行的字符串
	 * 
	 * @param content
	 * @return
	 */
	public static final String hexStr2Str(String content) {
		byte[] byteRresult = new byte[content.length() / 2];
		for (int i = 0; i < content.length() / 2; i++) {
			int high = Integer
					.parseInt(content.substring(i * 2, i * 2 + 1), 16);
			int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2),
					16);
			byteRresult[i] = (byte) (high * 16 + low);
		}
		return new String(byteRresult);
	}


}
